from sympy.geometry.plane import Plane
from sympy.geometry.point import Point
from sympy.geometry.polygon import Polygon, RegularPolygon
from sympy import S
from sympy.core import Number
from sympy.core.containers import Tuple
from sympy.geometry.entity import GeometrySet

# from .polyhedron import Polyhedron
# from .polygon3D import Polygon3D
# from .utils import angle, points_to_3d, points_to_2d
from geometry3D.polyhedron import Polyhedron
from geometry3D.polygon3D import Polygon3D, RegularPolygon3D
from geometry3D.utils import angle, points_to_3d, points_to_2d, polygon_to_plane


class Prism(Polyhedron):
    """
    This is a class that use to build the pyramid.
    """

    def __new__(cls, polygon: Polygon, vector, *args, **kwargs):
        if vector in S.Reals:
            vector = Point(0, 0, vector)

        if not isinstance(polygon, (Polygon, Polygon3D)):
            raise ValueError("the bottom should be a polygon.")
        bottom_points = points_to_3d(polygon.vertices)
        bottom_1_points = list(map(lambda point: point + vector, bottom_points))
        return super().__new__(cls, *bottom_points, *bottom_1_points)

    @property
    def n(self):
        return int(len(self.vertices) / 2)

    @property
    def bottoms(self) -> Tuple:
        n = self.n
        return Tuple(
            Polygon(*points_to_2d(self.args[0:3])),
            Polygon(*points_to_2d(self.args[n : n + 3])),
        )

    @property
    def bottom_planes(self) -> Tuple:
        return Tuple(
            *list(map(lambda polygon: polygon_to_plane(polygon), self.bottoms))
        )

    @property
    def height(self) -> Number:
        return self.bottom_planes[0].distance(self.vertices[self.n])

    @property
    def volume(self) -> Number:
        return self.bottom.area * self.height

    @property
    def side_polygons(self) -> Tuple:
        side_polygons = []
        n = self.n
        vertices = self.vertices
        for i in range(0, n - 1):
            side_polygons.append(
                Polygon(
                    vertices[i], vertices[i + 1], vertices[i + n + 1], vertices[i + n]
                )
            )
        side_polygons.append(
            Polygon(vertices[0], vertices[n - 1], vertices[2 * n - 1], vertices[n])
        )
        return Tuple(*side_polygons)

    @property
    def side_planes(self) -> Tuple:
        return Tuple(
            *list(map(lambda polygon: polygon_to_plane(polygon), self.side_polygons))
        )
